package com.ilarele.shopping.server.trust;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;

import com.ilarele.shopping.Product;
import com.ilarele.shopping.server.MysqlBasicOps;
import com.ilarele.shopping.server.User;

public class TrustAlgorithm {
	private MysqlBasicOps sqlOps;
	// id, users
	private HashMap<String, User> users = new HashMap<String, User>();

	public TrustAlgorithm(MysqlBasicOps sqlOps) {
		this.sqlOps = sqlOps;

		// read social network
		ResultSet rs = sqlOps.getPreferencesRS();
		try {
			// for each client from preferences table
			while (rs.next()) {
				// read his friend list
				String idUser = rs.getString("id_client");
				String friendList = rs.getString("friends_list");
				HashMap<String, Float> friends = sqlOps
						.parseFriends(friendList);
				User user = new User(idUser, friends);
				users.put(idUser, user);
			}
			rs.close();
		} catch (SQLException e) {
			e.printStackTrace();
			if (rs != null)
				try {
					rs.close();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
		}
	}

	public float getTrustFromTo(String fromId, String toId, String idProd,
			HashSet<String> alreadyChecked) {
		if (alreadyChecked.contains(fromId))
			return 0;
		alreadyChecked.add(fromId);
		// TODO stop la un nivel de adancime
		if (fromId.equals(toId))
			return 0;
		// breath first
		ArrayList<Entry<String, Float>> nextLevelFriends = new ArrayList<Entry<String, Float>>();
		User fromUser = users.get(fromId);
		if (fromUser != null && fromUser.friends != null) {
			Set<Entry<String, Float>> fromUserFriends = fromUser.friends
					.entrySet();
			for (Entry<String, Float> friendEntry : fromUserFriends) {
				String friendId = friendEntry.getKey();
				if (friendId.equals(toId)) {
					Float friendPoints = friendEntry.getValue();
					Float interesRate = sqlOps.getInteresRate(toId, idProd);
					if (interesRate > 1)
						interesRate = 1f;
					friendPoints = friendPoints
							/ users.get(toId).numberContributions;
					if (friendPoints > 1)
						friendPoints = 1f;
					return friendPoints * interesRate;
				}
				nextLevelFriends.add(friendEntry);
			}
		}

		float secondSum = 0;
		int secondNo = 0;
		for (Entry<String, Float> secondLevel : nextLevelFriends) {
			String secondId = secondLevel.getKey();
			Float secondPoints = secondLevel.getValue();
			// (second's rate) * (rate to toId)
			Float interesRate = sqlOps.getInteresRate(toId, idProd);
			if (interesRate > 1)
				interesRate = 1f;
			secondPoints = secondPoints / users.get(toId).numberContributions;
			if (secondPoints > 1)
				secondPoints = 1f;
			secondSum += secondPoints
					* getTrustFromTo(secondId, toId, idProd,
							new HashSet<String>(alreadyChecked)) * interesRate;
			secondNo++;
		}
		if (secondNo == 0)
			return 0;
		return secondSum / secondNo;
	}

	public void addPointsByFriendship(ArrayList<Product> prodList,
			String idClient) {
		for (Product product : prodList) {
			HashSet<String> prodUsersId = sqlOps
					.getUsersInTouchWithProd(product.idProd);
			for (String prodUserId : prodUsersId) {
				product.pointsTrust += getTrustFromTo(idClient, prodUserId,
						product.idProd, new HashSet<String>());
			}
		}
	}

	public void incFriendship(String idUser, String idFriend, float points) {
		if (idUser.equals(idFriend))
			return;
		User currentUser = users.get(idUser);
		HashMap<String, Float> userFriends = currentUser.friends;
		Float friendPoints = userFriends.get(idFriend);
		if (friendPoints != null)
			userFriends.put(idFriend, friendPoints + points);
		else
			userFriends.put(idFriend, points);
		currentUser.numberContributions += points;

	}

	public void addUser(String idClient) {
		User userExists = users.get(idClient);
		if (userExists == null)
			users.put(idClient, new User(idClient, null));
	}
}
